﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Security.Cryptography;
using System.Net;
using System.Text.RegularExpressions;

namespace WebQQ批量挂QQ软件
{
    /// <summary>
    /// webqq3.0操作帮助类
    /// </summary>
    public class QQHelp
    {
        /// <summary>
        /// Webqq3.0登录方法
        /// </summary>
        /// <param name="qq">qq号码</param>
        /// <param name="password">qq密码</param>
        /// <param name="verifycode">验证码</param>
        /// <param name="webqq_type">登录状态类型</param>
        /// <param name="cookie">登录成功后cookie</param>
        /// <param name="ptwebqq">ptwebqq</param>
        /// <param name="clientId">clientId</param>
        /// <param name="psessionid">psessionid</param>
        /// <returns>是否登录成功</returns>
        public static bool Login(string qq, string password, string verifycode, string webqq_type,
            ref string cookie, ref string ptwebqq, ref string clientId, ref string psessionid, ref string vfwebqq, ref string msg)
        {
            bool success = false;

            string url = "http://ptlogin2.qq.com/login?" +
               "u=" + qq + "&p=" + getPassword(password, verifycode) + "&verifycode=" + verifycode + "&webqq_type=" + GetLoginState(true, webqq_type)
               + "&remember_uin=1&login2qq=1&aid=1003903&u1=http%3A%2F%2Fweb.qq.com%2Floginproxy.html%3Flogin2qq%3D1%26webqq_type%3D10&h=1&ptredirect=0&ptlang=2052&from_ui=1&pttype=1&dumy=&fp=loginerroralert&mibao_css=m_webqq";

            string html = HttpHelp.GetHtml(url, "", ref cookie, Encoding.UTF8, true);//ptuiCB('24','0','','0','参数非法(563086944)');\r\n
            html = html.Remove(0, html.LastIndexOf(",") + 2);
            html = html.Substring(0, html.IndexOf("'"));

            if (html.Contains("参数非法") || html.Contains("您输入的验证码有误，请重试。"))
            {
                msg = html;
                success = false;
            }
            else if (html.Contains("登录成功！"))
            {
                //登录成功的话要获取Cookie值，后面要用，尤其是skey，ptcz，ptwebqq,还有一个参数verifysession，就是验证码的那个Cookie，也要用。
                ptwebqq = cookie.Remove(0, cookie.IndexOf("ptwebqq=") + 8);
                ptwebqq = ptwebqq.Substring(0, ptwebqq.IndexOf(";"));
                clientId = GenerateClientID();

                string postData = string.Format("r=%7B%22status%22%3A%22" + GetLoginState(false, webqq_type) 
                    + "%22%2C%22ptwebqq%22%3A%22{0}%22%2C%22passwd_sig%22%3A%22%22%2C%22clientid%22%3A%22{1}%22%2C%22psessionid%22%3Anull%7D&clientid={2}&psessionid=null", 
                    ptwebqq, clientId, clientId);

                cookie = FindCookie(cookie);

                WebHeaderCollection header = null;
                string html2 = HttpHelp.GetHtml("http://d.web2.qq.com/channel/login2", "http://d.web2.qq.com/proxy.html?v=20110331002&callback=2", postData,
                    ref cookie, true, out header, "application/x-www-form-urlencoded; charset=UTF-8", Encoding.UTF8, false);

                if (html2.Contains("{\"retcode\":0"))
                {
                    html2 = html2.Remove(0, html2.IndexOf("vfwebqq\":\"") + 10);
                    vfwebqq = html2.Substring(0, html2.IndexOf("\""));
                    html2 = html2.Remove(0, html2.IndexOf("psessionid\":\"") + 13);
                    psessionid = html2.Substring(0, html2.IndexOf("\""));
                    msg = "成功登录上线";
                    success = true;
                }
                else
                {
                    //{\"retcode\":113,\"t\":0}\r\n
                    //{\"retcode\":103,\"errmsg\":\"\"}
                    msg = "登录成功，上线失败！";
                }
            }
            else
            {
                success = false;
                msg = html;
            }
            return success;
        }

        /// <summary>
        /// 判断登录qq号是否需要输入验证码
        /// </summary>
        /// <param name="qq">qq号码</param>
        /// <param name="cookie">获取验证码返回cookie</param>
        /// <returns>验证码信息</returns>
        public static string GetVerfyCode(string qq, out string cookie)
        {
            string verifycode = string.Empty;

            string url = "http://ptlogin2.qq.com/check?uin=" + qq + "&appid=1003903&r=" + new Random().NextDouble();
            string html = HttpHelp.GetHtml(url, out cookie, Encoding.UTF8);//ptui_checkVC('0','!8QH');如果是ptui_checkVC('1',则表示要输入验证码
            string needVerfyCode = html.Substring(html.IndexOf("ptui_checkVC('") + 14, 1);

            if (needVerfyCode == "0")
            {
                html = html.Remove(0, html.IndexOf("','") + 3);
                verifycode = html.Substring(0, html.IndexOf("')"));
            }
            else
            {
            Start:
                string securityUrl = "http://captcha.qq.com/getimage?aid=1003903&r=" + new Random().NextDouble() + "&uin=" + qq + "&vc_type=";
                Image image = HttpHelp.GetSecurityImage(securityUrl, "", ref cookie, true);
                
                frmVerifycode frmVerifyCode = new frmVerifycode(image, qq);
                frmVerifyCode.ShowDialog();
                verifycode = frmVerifyCode.verifycode;
                if (frmVerifyCode.changeOne)//看不清，换一张
                {
                    goto Start;
                }
            }
            return verifycode;
        }

        /// <summary>
        /// 获取登录状态
        /// </summary>
        /// <param name="firstSteps">true表示是开始登录状态，false表示是登录成功后的状态</param>
        /// <returns></returns>
        private static string GetLoginState(bool firstSteps, string statusName)
        {
            string state = "";
            switch (statusName)
            {
                case "我在上线": state = firstSteps == true ? "10" : "online"; break;
                case "Q我吧": state = firstSteps == true ? "60" : "callme"; break;
                case "离开": state = firstSteps == true ? "30" : "away"; break;
                case "忙碌": state = firstSteps == true ? "50" : "busy"; break;
                case "请勿打扰": state = firstSteps == true ? "70" : "silent"; break;
                case "隐身": state = firstSteps == true ? "40" : "hidden"; break;
                case "离线": state = "offline"; break;
            }
            return state;
        }

        /// <summary>
        /// 更改登录状态
        /// </summary>
        /// <param name="psessionid">如果离线后再次更改为其他状态，那么要重新保存psessionid和vfwebqq</param>
        /// <param name="clientId">clientId</param>
        /// <param name="ptwebqq">ptwebqq</param>
        /// <param name="upChangeStatus">上一次状态</param>
        /// <param name="cookie">登录成功后的cookie</param>
        /// <param name="webqq_type">本次更改的状态</param>
        /// <param name="msg">更改消息</param>
        public static bool ChangeLoginState(ref string psessionid, string clientId, string ptwebqq, string upChangeStatus,
          string cookie, string webqq_type, ref string msg)
        {
            bool success = false;
            string html = string.Empty;
            if (upChangeStatus == "离线")
            {
                string postData = string.Format("r=%7B%22status%22%3A%22" + GetLoginState(false, webqq_type) 
                    + "%22%2C%22ptwebqq%22%3A%22{0}%22%2C%22passwd_sig%22%3A%22%22%2C%22clientid%22%3A%22{1}%22%2C%22psessionid%22%3Anull%7D&clientid={2}&psessionid={3}",
                    ptwebqq, clientId, clientId, psessionid);

                WebHeaderCollection header = null;
                html = HttpHelp.GetHtml("http://d.web2.qq.com/channel/login2", "http://d.web2.qq.com/proxy.html?v=20110331002&callback=2", postData, 
                    ref cookie, true, out header, "application/x-www-form-urlencoded; charset=UTF-8", Encoding.UTF8, false);
            }
            else
            {
                string url = "http://d.web2.qq.com/channel/change_status2?newstatus=" + GetLoginState(false, webqq_type) + "&clientid=" + clientId 
                    + "&psessionid=" + psessionid + "&t=1309925582216";
                html = HttpHelp.GetHtml(url, "http://d.web2.qq.com/proxy.html?v=20110331002&callback=2", ref cookie, Encoding.UTF8, false);
            }

            if (html.Contains("\"retcode\":0"))//{"retcode":0,"result":"ok"}
            {
                msg = webqq_type + "状态更改成功！";
                //重新保存psessionid
                if (upChangeStatus == "离线")
                {
                    html = html.Remove(0, html.IndexOf("psessionid\":\"") + 13);
                    psessionid = html.Substring(0, html.IndexOf("\""));
                }
                upChangeStatus = webqq_type;
                success = true;
            }
            else
            {
                msg = webqq_type + "状态更改失败！" + html;
                success = false;
            }
            return success;
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="cookie">登陆后的cookie信息</param>
        /// <param name="toQQ">发送给谁</param>
        /// <param name="content">发送内容</param>
        /// <param name="clientId">clientId</param>
        /// <param name="psessionid">psessionid</param>
        /// <returns>是否发送成功</returns>
        public static bool SendMsg(string cookie, string toQQ, string content, string clientId, string psessionid)
        {
            WebHeaderCollection header;
            string postData = "r=%7B%22to%22%3A" + toQQ + "%2C%22face%22%3A681%2C%22content%22%3A%22%5B%5C%22" + content
                + "%5C%22%2C%5B%5C%22font%5C%22%2C%7B%5C%22name%5C%22%3A%5C%22%E5%AE%8B%E4%BD%93%5C%22%2C%5C%22size%5C%22" +
                "%3A%5C%2210%5C%22%2C%5C%22style%5C%22%3A%5B0%2C0%2C0%5D%2C%5C%22color%5C%22%3A%5C%22000000%5C%22%7D%5D%5D%22%2C%22msg_id%22%3A"
                + new Random().Next(10000000) + "%2C%22clientid%22%3A%22"
                + clientId + "%22%2C%22psessionid%22%3A%22" + psessionid + "%22%7D&clientid=" + clientId + "&psessionid=" + psessionid;

            string html = HttpHelp.GetHtml("http://d.web2.qq.com/channel/send_buddy_msg2", "http://d.web2.qq.com/proxy.html?v=20110331002&callback=2", postData, ref cookie, true, out header, "application/x-www-form-urlencoded; charset=UTF-8", Encoding.UTF8, false);
            if (html.Contains("result\":\"ok\""))
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// 根据uid获取好友真实QQ号码
        /// </summary>
        /// <param name="cookie">登录后的cookie</param>
        /// <param name="uid"></param>
        /// <param name="vfwebqq"></param>
        /// <returns></returns>
        public static string GetQQByUid(string cookie, string uid, string vfwebqq)
        {
            string qq = string.Empty;
            string url = "http://s.web2.qq.com/api/get_friend_uin2?tuin=" + uid + "&verifysession=&type=1&code=&vfwebqq=" + vfwebqq + "&t=" + DateTime.Now.ToFileTime();
            string html = HttpHelp.GetHtml(url, "http://s.web2.qq.com/proxy.html?v=20110412001&callback=1&id=2", ref cookie, Encoding.UTF8, false);
            if (html.Contains("{\"retcode\":0,\"result\":"))
            {
                //{"retcode":0,"result":{"account":740156554,"uin":4267385890}}
                html = html.Remove(0, html.IndexOf("account\":") + 9);
                qq = html.Substring(0, html.IndexOf(",\"uin"));
            }
            return qq;
        }

        /// <summary>
        /// 获得在线时间，注意大于30分钟，在线时长才会改变，有缓存时间
        /// </summary>
        /// <param name="cookie">附加cookie信息</param>
        /// <returns>120分钟</returns>
        public static string GetOnlineTimes(string cookie)
        {
            string onlineTimes = string.Empty;

            string url = "http://id.qq.com/cgi-bin/qqlevel?t=" + Convert.ToInt32(new Random().Next(1000) / 121).ToString();
            string html = HttpHelp.GetHtml(url, "http://id.qq.com/level/level.html", ref cookie, Encoding.UTF8, false);
            if (html.Contains("onlineTimes"))
            {
                html = html.Remove(0, html.IndexOf("onlineTimes\":") + 13);
                onlineTimes = html.Substring(0, html.IndexOf(",\"remainDays")) + "分钟";
            }
            return onlineTimes;
        }

        /// <summary>
        /// 退出登录
        /// </summary>
        /// <param name="cookie">登录成功后cookie</param>
        /// <param name="clientId">clientId</param>
        /// <param name="psessionid">psessionid</param>
        /// <param name="msg">返回消息</param>
        public static void Logout(string cookie, string clientId, string psessionid, ref string msg)
        {
            string url = "http://d.web2.qq.com/channel/logout2?clientid=" + clientId + "&psessionid=" + psessionid + "&t=" + DateTime.Now.ToFileTime();
            string html = HttpHelp.GetHtml(url, "http://d.web2.qq.com/proxy.html?v=20110331002&callback=2", ref cookie, Encoding.UTF8, false);
            if (html.Contains("\"retcode\":0"))//{"retcode":0,"result":"ok"}
            {
                msg = "退出成功";
            }
            else
            {
                msg = "退出失败" + html;
            }
        }

        /// <summary>
        /// 将返回cookie格式化
        /// </summary>
        /// <param name="cookie">cookie</param>
        /// <returns>格式化cookie</returns>
        private static string FindCookie(string cookie)
        {
            string _tmpcookie = "";
            foreach (string strtemp in Regex.Split(cookie, ","))
            {
                Match match = Regex.Match(strtemp, @"^\w[^=]+=[^;]+");
                if (match.Success)
                {
                    _tmpcookie = _tmpcookie + match.Value + ";";
                }
            }
            return _tmpcookie;
        }

        #region 登录成功后，上线时clientid生成

        public static string GenerateClientID()
        {
            return new Random(Guid.NewGuid().GetHashCode()).Next(0, 99) + "" + GetTime(DateTime.Now) / 1000000;
        }

        public static long GetTime(DateTime dateTime)
        {
            DateTime startDate = new DateTime(1970, 1, 1);
            DateTime endDate = dateTime.ToUniversalTime();
            TimeSpan span = endDate - startDate;
            return (long)(span.TotalMilliseconds + 0.5);
        }

        #endregion

        #region QQ密码加密

        /// <summary>
        /// 三次md5加密
        /// </summary>
        /// <param name="password">原始密码</param>
        /// <param name="verifycode">验证码</param>
        /// <returns>加密后密码</returns>
        public static string getPassword(string password, string verifycode)
        {
            return md5(md5_3(password).ToUpper() + verifycode.ToUpper()).ToUpper();
        }

        private static string binl2hex(byte[] buffer)
        {
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < buffer.Length; i++)
            {
                builder.Append(buffer[i].ToString("x2"));
            }
            return builder.ToString();
        }

        private static string md5_3(string input)
        {
            MD5 md = MD5.Create();
            byte[] buffer = md.ComputeHash(Encoding.Default.GetBytes(input));
            buffer = md.ComputeHash(buffer);
            buffer = md.ComputeHash(buffer);
            return binl2hex(buffer);
        }

        private static string md5(string input)
        {
            byte[] buffer = MD5.Create().ComputeHash(Encoding.Default.GetBytes(input));
            return binl2hex(buffer);
        }

        #endregion
    }
}
